﻿using BaseFramework;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Xml.Serialization;

namespace LiteFramework
{
    /// <summary>
    /// 可持久实体
    /// </summary>
    public abstract class Entity<T> : Bag, INotifyPropertyChanged, IDataErrorInfo, ICloneable, IChangeTracking
    where T : class
    {
        [NonSerialized]
        protected static readonly Dictionary<Type, System.Web.UI.Pair> _typeInfo = new Dictionary<Type, System.Web.UI.Pair>();
        [NonSerialized]
        protected static readonly object _typeInfo_Lock = new object();

        protected readonly ConcurrentDictionary<string, object> _hash = new ConcurrentDictionary<string, object>();

        public virtual bool IsNew()
        {
            PropertyInfo proInfo = null;
            string unsavedValue = null;

            Type type = this.GetType();
            System.Web.UI.Pair pair;
            if (!_typeInfo.TryGetValue(type, out pair))
            {
                lock (_typeInfo_Lock)
                {
                    if (!_typeInfo.TryGetValue(type, out pair))
                    {
                        PropertyInfo[] pis = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                        foreach (PropertyInfo pi in pis)
                        {
                            object[] atts = pi.GetCustomAttributes(typeof(PrimaryKeyAttribute), true);
                            if (atts.Length != 0)
                            {
                                proInfo = pi;

                                PrimaryKeyAttribute pka = (PrimaryKeyAttribute)atts[0];
                                unsavedValue = pka.UnsavedValue;

                                pair = new System.Web.UI.Pair(proInfo, unsavedValue);
                                _typeInfo.Add(type, pair);

                                break;
                            }
                        }

                        // 不存在主键属性，也添加到缓存
                        if (!_typeInfo.ContainsKey(type)) _typeInfo.Add(type, null);
                    }
                }
            }

            if (pair == null) return false;

            proInfo = pair.First as PropertyInfo;
            unsavedValue = pair.Second as string;
            if (proInfo != null && unsavedValue != null)
            {
                object value = proInfo.GetValue(this, null);
                if (value == null) // 对象的键值空时，认为是新的对象
                    return true;
                else
                    return (value.ToString() == unsavedValue);
            }

            return false;
        }

        private KeyedBag _internalBag = null;
        /// <summary>
        /// 实体的内部数据包装
        /// </summary>
        [XmlIgnore]
        [Description("实体的内部数据包装")]
        [DisplayName("内部数据")]
        public KeyedBag InternalBag
        {
            get
            {
                if (_internalBag == null)
                    _internalBag = new KeyedBag(this);

                return _internalBag;
            }
        }


        #region Copy
        /// <summary>
        /// 深度拷贝
        /// </summary>
        /// <returns></returns>
        public virtual T Copy()
        {
            T handle;
            PersistentEntity<T> entity = CreateInstance(out handle);

            if (entity != null)
            {
                this.CopyTo(entity);
                return handle;
            }
            else
            {
                return default(T);
            }
        }
        #endregion

        #region INotifyPropertyChanged 成员
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion

        #region IDataErrorInfo 成员

        [XmlIgnore()]
        string IDataErrorInfo.Error
        {
            get
            {
                //string[] errors = this.GetErrorMessages();
                //if (errors != null && errors.Length != 0)
                //    return String.Join(Environment.NewLine, errors);
                //else
                return String.Empty;
            }
        }

        [XmlIgnore()]
        string IDataErrorInfo.this[string propertyName]
        {
            get
            {
                string error = null;//= this.GetErrorMessage(propertyName);
                return (error != null ? error : String.Empty);
            }
        }

        #endregion

        #region ICloneable 成员
        public object Clone()
        {
            return this.Copy();
        }
        #endregion

        #region Compare

        /// <summary>
        /// IComparer 成员
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        //public override int Compare(object x, object y)
        //{
        //    T tx = x as T;
        //    T ty = y as T;

        //    if (object.ReferenceEquals(tx, ty)) return 0;
        //    if (tx == null) return 1;
        //    if (ty == null) return -1;

        //    int result = this.onComparing(tx, ty);
        //    if (result != int.MaxValue) return result;

        //    return base.Compare(x, y);
        //}

        /// <summary>
        /// 在比较两个对象时调用。其中x不为空，且y不为空
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        protected virtual int onComparing(T x, T y)
        {
            return int.MaxValue;
        }

        #endregion

        #region Private

        private static PersistentEntity<T> CreateInstance(out T handle)
        {
            //handle = Activator.CreateInstance<T>();
            handle = Activator.CreateInstance(typeof(T), true) as T;
            return handle as PersistentEntity<T>;
        }

        #endregion

        void IChangeTracking.AcceptChanges()
        {
            throw new NotImplementedException();
        }

        bool IChangeTracking.IsChanged
        {
            get { throw new NotImplementedException(); }
        }

        public override string ToString()
        {
            //return base.ToString();
            return this.GetType().Name;
        }

    }
}
